package com.yyplatform.admin.platform.role.service.impl;

import static com.yyplatform.admin.infra.exception.ErrorCode.DELETE_DATA_ERROR_SUBORDINATE_EXISTS;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.yyplatform.admin.infra.exception.ErrorCode;
import com.yyplatform.admin.infra.exception.ServiceException;
import com.yyplatform.admin.infra.utils.TreeUtils;
import com.yyplatform.admin.platform.role.model.dto.SysMenuDTO;
import com.yyplatform.admin.platform.role.model.dto.SysMenuFormDTO;
import com.yyplatform.admin.platform.role.model.entity.SysMenuEntity;
import com.yyplatform.admin.platform.role.model.entity.SysRoleMenuEntity;
import com.yyplatform.admin.platform.role.model.enums.MenuTypeEnum;
import com.yyplatform.admin.platform.role.repository.SysMenuRepository;
import com.yyplatform.admin.platform.role.repository.SysRoleMenuRepository;
import com.yyplatform.admin.platform.role.service.SysMenuService;

import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Created on 2024-09-14
 */
@Service
@Slf4j
@AllArgsConstructor
public class SysMenuServiceImpl implements SysMenuService {

    private SysMenuRepository sysMenuRepository;
    private SysRoleMenuRepository sysRoleMenuRepository;
    private ModelMapper modelMapper;


    @Override
    public List<SysMenuDTO> listAllForTree() {
        List<SysMenuEntity> dblist = sysMenuRepository.findAll();

        List<SysMenuDTO> dtoList = dblist.stream()
                .map(e -> modelMapper.map(e, SysMenuDTO.class))
                .toList();

        return TreeUtils.build(dtoList, 0L);
    }


    @Override
    public SysMenuDTO get(Long id) {
        SysMenuEntity entity = sysMenuRepository.getReferenceById(id);
        return modelMapper.map(entity, SysMenuDTO.class);
    }


    @Override
    public void save(SysMenuFormDTO sysMenuFormDTO) {
        SysMenuEntity entity = modelMapper.map(sysMenuFormDTO, SysMenuEntity.class);
        log.info("entity", entity.toString());


        if (StringUtils.equals(entity.getMenuType(), MenuTypeEnum.MENU_TYPE_BUTTON.getValue())
                && StringUtils.startsWith(entity.getName(), "@")) {
            saveBatchByControllerName(entity.getPid(), entity.getName());
            return;
        }

        sysMenuRepository.save(entity);

    }


    @Override
    public void saveBatchByControllerName(Long id, List<String> cname) {
        // 删除历史数据
        List<SysMenuEntity> entityList = sysMenuRepository.findByPid(id);

        for (SysMenuEntity sysMenuEntity : entityList) {
            log.info("delete sysMenuEntity by pid {}, entity {}", id, sysMenuEntity.getName());
        }
        //sysMenuRepository.deleteAll(entityList);

        cname.forEach(cc -> {
            saveControllerName(id, cc);
        });

    }


    public void saveBatchByControllerName(Long id, String controllerName) {
        // 删除历史数据
        List<SysMenuEntity> entityList = sysMenuRepository.findByPid(id);
        for (SysMenuEntity sysMenuEntity : entityList) {
            log.info("delete sysMenuEntity by pid {}, entity {}", id, sysMenuEntity.getName());
        }
        //sysMenuRepository.deleteAll(entityList);


        saveControllerName(id, controllerName);

    }

    private void saveControllerName(Long id, String controllerName) {
        try {
            Class<?> classController = Class.forName(controllerName.replace("@", ""));

            Method[] methods = classController.getMethods();

            int idx = 1;
            for (Method method : methods) {
                if (method.isAnnotationPresent(RequiresPermissions.class)) {
                    RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
                    Operation operation = method.getAnnotation(Operation.class);

                    SysMenuEntity sysMenuEntity = new SysMenuEntity();

                    sysMenuEntity.setAlwaysShow(false);
                    sysMenuEntity.setComponent("");
                    sysMenuEntity.setEnabled(true);
                    sysMenuEntity.setIcon("");
                    sysMenuEntity.setKeepAlive(false);
                    sysMenuEntity.setMenuType(MenuTypeEnum.MENU_TYPE_BUTTON.getValue());
                    sysMenuEntity.setOrderNum(idx * 100);
                    sysMenuEntity.setParams(Collections.EMPTY_LIST);
                    sysMenuEntity.setRedirect("");
                    sysMenuEntity.setRemark("");
                    sysMenuEntity.setRoutePath("");
                    sysMenuEntity.setRouteName("");

                    sysMenuEntity.setVisible(false);

                    sysMenuEntity.setPid(id);
                    sysMenuEntity.setName(operation.summary());
                    sysMenuEntity.setPermissions(requiresPermissions.value()[0]);


                    sysMenuRepository.save(sysMenuEntity);

                    idx = idx + 1;
                }
            }

        } catch (ClassNotFoundException e) {
            throw ServiceException.of(ErrorCode.UNKNOW_ERROR);
        }
    }


    @Override
    public void update(SysMenuFormDTO sysDeptFormDTO) {
        SysMenuEntity entity = modelMapper.map(sysDeptFormDTO, SysMenuEntity.class);


        sysMenuRepository.save(entity);

    }

    @Override
    public void delete(Long id) {
        List<SysMenuEntity> list = sysMenuRepository.findByPid(id);
        if (!list.isEmpty()) {
            throw ServiceException.of(DELETE_DATA_ERROR_SUBORDINATE_EXISTS);
        }

        List<SysRoleMenuEntity> sysRoleMenuEntityList = sysRoleMenuRepository.findByMenuId(id);

        if (!CollectionUtils.isEmpty(sysRoleMenuEntityList)) {
            throw ServiceException.of(DELETE_DATA_ERROR_SUBORDINATE_EXISTS);
        }

        sysMenuRepository.deleteById(id);
    }

    @Override
    public List<SysMenuDTO> listForOptions(Boolean onlyRoot) {
        if (onlyRoot) {
            List<SysMenuEntity> list = sysMenuRepository.findByPid(0L);

            return list.stream()
                    .map(e -> modelMapper.map(e, SysMenuDTO.class))
                    .map(e -> {
                        e.setChildren(null);
                        e.setHasChildren(false);
                        return e;
                    })
                    .toList();
        }

        return listAllForTree();
    }

}
